package aliyun

import (
	"bytes"
	"context"
	"fmt"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gfile"
	"io"
	"os"
	"time"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

var AliyunOss = &aliyunOss{}

type aliyunOss struct {
	endpoint string
}

func (receiver *aliyunOss) GetEndpoint(slug, endpointType string) string {
	switch endpointType {
	case "accelerate":
		slug = "accelerate"
	case "public":
	case "ecs", "vpc":
		fallthrough
	default:
		slug += "-internal"
	}

	endpoint := "oss-" + slug + ".aliyuncs.com"
	return endpoint
}
func (receiver *aliyunOss) Bucket(ctx context.Context, ossConfig map[string]string) *oss.Bucket {
	config, _ := g.Cfg().Get(ctx, "aliyun."+ossConfig["account"])
	credentials := config.MapStrStr()
	endpoint := receiver.GetEndpoint(ossConfig["region"], ossConfig["endpointType"])
	receiver.endpoint = endpoint

	client, err := oss.New(endpoint, credentials["accessKeyId"], credentials["accessKeySecret"])
	if err != nil {
		panic(err)
	}

	bucket, err := client.Bucket(ossConfig["bucket"])
	if err != nil {
		panic(err)
	}

	return bucket
}

func (receiver *aliyunOss) UploadContent(ctx context.Context, ossConfig map[string]string, object string, reader io.Reader) (*gjson.Json, error) {
	bucket := receiver.Bucket(ctx, ossConfig)

	resp, err := receiver.PutObject(bucket, ossConfig["prefix"]+object, reader)
	if err != nil {
		panic(err)
	}

	buf := new(bytes.Buffer)
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		panic(err)
	}
	j := gjson.New(buf.String())
	defer resp.Close()

	return j, nil
}
func (receiver *aliyunOss) UploadFile(ctx context.Context, ossConfig map[string]string, object, filePath string) (*gjson.Json, error) {
	bucket := receiver.Bucket(ctx, ossConfig)
	object = ossConfig["prefix"] + object

	resp, err := receiver.PutObjectFromFile(bucket, object, filePath)
	if err != nil {
		panic(err)
	}

	buf := new(bytes.Buffer)
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		panic(err)
	}
	j := gjson.New(buf.String())
	defer resp.Close()

	ossPath := fmt.Sprintln("oss://%s/%s/%s", receiver.endpoint, bucket, object)
	j.Append("path", ossPath)

	return j, nil
}
func (receiver *aliyunOss) DownloadContent(ctx context.Context, ossConfig map[string]string) (string, error) {
	var err error

	bucket := receiver.Bucket(ctx, ossConfig)

	for i := 0; i < 3; i++ {
		context, err := bucket.GetObject(ossConfig["object"])
		if err == nil {
			buf := new(bytes.Buffer)
			_, err = buf.ReadFrom(context)
			if err != nil {
				panic(err)
			}

			return buf.String(), nil
		}

		time.Sleep(1 * time.Second)
	}

	return "", err
}
func (receiver *aliyunOss) DownloadFile(ctx context.Context, ossConfig map[string]string, localPath string) error {
	var err error

	bucket := receiver.Bucket(ctx, ossConfig)

	directoryName := gfile.Dir(localPath)
	if !gfile.Exists(directoryName) {
		err = gfile.Mkdir(directoryName)
		if err != nil {
			return err
		}
	}

	for i := 0; i < 3; i++ {
		err = bucket.GetObjectToFile(ossConfig["object"], localPath)
		if err == nil {
			return nil
		}

		time.Sleep(1 * time.Second)
	}

	return err
}

func (receiver *aliyunOss) PutObject(bucket *oss.Bucket, objectKey string, reader io.Reader, options ...oss.Option) (*oss.Response, error) {
	opts := oss.AddContentType(options, objectKey)
	request := &oss.PutObjectRequest{
		ObjectKey: objectKey,
		Reader:    reader,
	}

	return bucket.DoPutObject(request, opts)
}
func (receiver *aliyunOss) PutObjectFromFile(bucket *oss.Bucket, objectKey, filePath string, options ...oss.Option) (*oss.Response, error) {
	fd, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer fd.Close()

	opts := oss.AddContentType(options, objectKey)
	request := &oss.PutObjectRequest{
		ObjectKey: objectKey,
		Reader:    fd,
	}

	return bucket.DoPutObject(request, opts)
}
